package handler

import (
	"log"
	"golang.org/x/net/context"
	oauth2 "git.oschina.net/fanbuchi/xgggh/oauth2/proto/oauth2"
	 "git.oschina.net/fanbuchi/xgggh/errs"
	"git.oschina.net/fanbuchi/xgggh/oauth2/service"
	"git.oschina.net/fanbuchi/xgggh/oauth2/model"
	"git.oschina.net/fanbuchi/xgggh/oauth2/db"
)

type Oauth2 struct{}

//--------------------Authorize begin---------------------
func (e *Oauth2) Authorize(ctx context.Context, req *oauth2.AuthorizeReq, rsp *oauth2.AuthorizeRsp) error {
	responseType:=req.ResponseType
	switch responseType {
	case oauth2.ResponseType_code.String():
		return code(ctx,req,rsp)
		break

	case oauth2.ResponseType_token.String():
		return token(ctx,req,rsp)
		break
	}
	return nil
}

func code(ctx context.Context, req *oauth2.AuthorizeReq, rsp *oauth2.AuthorizeRsp)error {
	return nil
}


func token(ctx context.Context, req *oauth2.AuthorizeReq, rsp *oauth2.AuthorizeRsp)error {
	return nil
}


//--------------------Token start-------------------------
//password grant
// micro query com.xgggh.srv.oauth2 Oauth2.Token '{"userName":"15108280908","password":"qw123456","grantType": "password","scope":"read","clientId":"e2b6abfc-3576-4fe7-9f69-1f647169a827","clientSecret":"e24e45c9-a7bc-4248-be78-17b32bea1544"}'
// micro query com.xgggh.srv.oauth2 Oauth2.Token '{"grantType":"refresh_token","refreshToken":"28cdd8a5-320b-47be-aac9-fca431636896","clientId":"41571387-5003-4379-4ba9-49e1ba341ed0","clientSecret":"secret_update"}'
// micro call com.xgggh.srv.oauth2 Oauth2.Token {\"userName\":\"15108280907\",\"password\":\"qw123456\",\"grantType\": \"password\",\"scope\":\"read\",\"clientId\":\"key1234567890\",\"clientSecret\":\"secret1234567890\"}

func (e *Oauth2) Token(ctx context.Context, req *oauth2.TokenReq, rsp *oauth2.TokenRsp) error {
	grantType:= req.GrantType
	switch grantType {
	case oauth2.GrantType_authorization_code.String():
	return authorizationCode(ctx,req,rsp)
		break

	case oauth2.GrantType_implicit.String():
	return implicit(ctx,req,rsp)
		break

	case oauth2.GrantType_password.String():
	return password(ctx,req,rsp)
		break

	case oauth2.GrantType_client_credentials.String():
	return clientCredential(ctx,req,rsp)
		break

	case oauth2.GrantType_refresh_token.String():
	return refreshToken(ctx,req,rsp)
		break
	}

	return errs.SupportAuthorize
}

//---------------------token end----------------------


//micro query com.xgggh.srv.oauth2 Oauth2Srv.Revoke '{"Token":"7f723cef-5b14-4afa-ab6e-7bf393ef8fe5","TokenTypeHint":"accessTokenHint","clientId":"41571387-5003-4379-4ba9-49e1ba341ed0","clientSecret":"secret_update"}'
//micro query com.xgggh.srv.oauth2 Oauth2Srv.Revoke '{"Token":"2fbfe5d6-4c70-4b34-9d05-0f7a24ad6552","TokenTypeHint":"refreshTokenHint","clientId":"41571387-5003-4379-4ba9-49e1ba341ed0","clientSecret":"secret_update"}'

func (e *Oauth2) Revoke(ctx context.Context, req *oauth2.RevokeReq, rsp *oauth2.RevokeRsp) error {
	tokenTypeHint := req.TokenTypeHint
	// Default to access token hint
	if tokenTypeHint == "" {
		tokenTypeHint = oauth2.TokenTypeHint_accessTokenHint.String()
	}

	if req.Token=="" {
		return errs.TokenNotFound
	}

	if req.ClientId=="" {
		return errs.ClientNotFound
	}
	if req.ClientSecret=="" {
		return errs.ClientSecretNotFound
	}

	_client,err:=service.ClientSearchByClientId(req.ClientId)
	if err !=nil{
		return err
	}
	//key|secret 非法
	if _client==nil||_client.Secret!=req.ClientSecret {
		return errs.ClientInvalid
	}

	switch tokenTypeHint {
	case oauth2.TokenTypeHint_accessTokenHint.String():
		accessToken, err := service.Authenticate(req.Token,_client)
		if err != nil {
			return  err
		}
		result,err:=service.NewRevokeFromAccessToken(accessToken,_client)
		if err !=nil{
			return err
		}
		rsp.Data=result
		return nil
		break
	case oauth2.TokenTypeHint_refreshTokenHint.String():
		if req.ClientId=="" {
			return errs.ClientNotFound
		}
		if req.ClientSecret=="" {
			return errs.ClientSecretNotFound
		}

		_client,err:=service.ClientSearchByClientId(req.ClientId)
		if err !=nil{
			return err
		}
		//key|secret 非法
		if _client==nil||_client.Secret!=req.ClientSecret {
			return errs.ClientInvalid
		}
		refreshToken, err := service.GetValidRefreshToken(req.Token, _client)
		if err != nil {
			return  err
		}
		 result,err:=service.NewRevokeFromRefreshToken(refreshToken,_client)
		if err != nil {
			return  err
		}
		rsp.Data=result
		return nil
	default:
		return  errs.TokenHintInvalid
	}
	return nil
}

//everbody logout
// micro query com.xgggh.srv.oauth2 Oauth2.Logout '{"token":"15108280908","password":"qw123456","grantType": "password","scope":"read","clientId":"e2b6abfc-3576-4fe7-9f69-1f647169a827","clientSecret":"e24e45c9-a7bc-4248-be78-17b32bea1544"}'


func (e *Oauth2) Logout(ctx context.Context, req *oauth2.LogoutReq, rsp *oauth2.LogoutRsp) error {
	if req.ClientId=="" {
		return errs.ClientNotFound
	}
	if req.ClientSecret=="" {
		return errs.ClientSecretNotFound
	}

	_client,err:=service.ClientSearchByClientId(req.ClientId)
	if err !=nil{
		return err
	}
	//key|secret 非法
	if _client==nil||_client.Secret!=req.ClientSecret {
		return errs.ClientInvalid
	}
	err=db.GetDB().
		Where("client_id=?",_client.ID).
		Where("token=?",req.Token).
		Delete(&model.AccessToken{}).//id is null
		Error
	if err ==nil{
		rsp.Msg="ok"
		return nil
	}
	return err
}


// Stream is a server side stream handler called via client.Stream or the generated client code
func (e *Oauth2) Stream(ctx context.Context, req *oauth2.StreamingRequest, stream oauth2.Oauth2_StreamStream) error {
	log.Printf("Received Example.Stream request with count: %d", req.Count)

	for i := 0; i < int(req.Count); i++ {
		log.Printf("Responding: %d", i)
		if err := stream.Send(&oauth2.StreamingResponse{
			Count: int64(i),
		}); err != nil {
			return err
		}
	}

	return nil
}

// PingPong is a bidirectional stream handler called via client.Stream or the generated client code
func (e *Oauth2) PingPong(ctx context.Context, stream oauth2.Oauth2_PingPongStream) error {
	for {
		req, err := stream.Recv()
		if err != nil {
			return err
		}
		log.Printf("Got ping %v", req.Stroke)
		if err := stream.Send(&oauth2.Pong{Stroke: req.Stroke}); err != nil {
			return err
		}
	}
}
